package discovery

import (
	"context"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"sync"
	"time"
)

const (
	// UDP 发现端口
	UDPDiscoveryPort = 8554
	// 广播间隔
	BroadcastInterval = 30 * time.Second
	// 缓存生存时间
	CacheLifeTime = 3 * BroadcastInterval
	// 魔数，用于识别数据包
	Magic = uint32(0x2EA7D90B)
)

// DeviceInfo 设备信息
type DeviceInfo struct {
	ID         string   `json:"id"`
	Name       string   `json:"name"`
	Addresses  []string `json:"addresses"`
	Port       int      `json:"port"`
	InstanceID int64    `json:"instance_id"`
}

// UDPDiscovery UDP 发现服务
type UDPDiscovery struct {
	deviceInfo    DeviceInfo
	conn          *net.UDPConn
	cache         map[string]cachedDevice
	cacheMutex    sync.RWMutex
	ctx           context.Context
	cancel        context.CancelFunc
	onDeviceFound func(DeviceInfo)
}

// cachedDevice 缓存的设备信息
type cachedDevice struct {
	info      DeviceInfo
	timestamp time.Time
}

// NewUDPDiscovery 创建新的 UDP 发现服务
func NewUDPDiscovery(deviceInfo DeviceInfo, onDeviceFound func(DeviceInfo)) *UDPDiscovery {
	ctx, cancel := context.WithCancel(context.Background())
	return &UDPDiscovery{
		deviceInfo:    deviceInfo,
		cache:         make(map[string]cachedDevice),
		ctx:           ctx,
		cancel:        cancel,
		onDeviceFound: onDeviceFound,
	}
}

// Start 启动 UDP 发现服务
func (u *UDPDiscovery) Start() error {
	log.Printf("🔧 开始启动 UDP 发现服务...")

	// 创建 UDP 连接
	addr := &net.UDPAddr{
		IP:   net.IPv4zero,
		Port: UDPDiscoveryPort,
	}

	log.Printf("📡 尝试绑定 UDP 端口 %d...", UDPDiscoveryPort)
	conn, err := net.ListenUDP("udp4", addr)
	if err != nil {
		log.Printf("❌ UDP 端口绑定失败: %v", err)
		return fmt.Errorf("failed to create UDP listener: %w", err)
	}
	u.conn = conn
	log.Printf("✅ UDP 端口绑定成功: %s", conn.LocalAddr().String())

	// 启动接收协程
	log.Printf("🔄 启动 UDP 接收协程...")
	go u.receiveAnnouncements()

	// 启动广播协程
	log.Printf("🔄 启动 UDP 广播协程...")
	go u.sendAnnouncements()

	log.Printf("✅ UDP discovery started on port %d", UDPDiscoveryPort)
	return nil
}

// Stop 停止 UDP 发现服务
func (u *UDPDiscovery) Stop() {
	if u.conn != nil {
		u.conn.Close()
	}
	u.cancel()
	log.Println("UDP discovery stopped")
}

// sendAnnouncements 发送设备广播
func (u *UDPDiscovery) sendAnnouncements() {
	ticker := time.NewTicker(BroadcastInterval)
	defer ticker.Stop()

	for {
		select {
		case <-u.ctx.Done():
			return
		case <-ticker.C:
			u.broadcastDeviceInfo()
		}
	}
}

// broadcastDeviceInfo 广播设备信息
func (u *UDPDiscovery) broadcastDeviceInfo() {
	log.Printf("📡 开始广播设备信息...")

	// 构建数据包
	packet := u.buildAnnouncementPacket()
	if packet == nil {
		log.Printf("❌ 构建广播数据包失败")
		return
	}
	log.Printf("📦 广播数据包大小: %d 字节", len(packet))

	// 获取所有网络接口的广播地址
	broadcastAddrs := u.getBroadcastAddresses()
	log.Printf("🌐 发现 %d 个广播地址: %v", len(broadcastAddrs), broadcastAddrs)

	sentCount := 0
	for _, addr := range broadcastAddrs {
		if u.sendPacket(packet, addr) {
			sentCount++
		}
	}

	log.Printf("📤 成功发送 %d/%d 个广播包", sentCount, len(broadcastAddrs))
}

// buildAnnouncementPacket 构建广播数据包
func (u *UDPDiscovery) buildAnnouncementPacket() []byte {
	// 序列化设备信息
	data, err := json.Marshal(u.deviceInfo)
	if err != nil {
		log.Printf("Failed to marshal device info: %v", err)
		return nil
	}

	// 构建数据包：魔数 + 数据长度 + 数据
	packet := make([]byte, 4+4+len(data))
	binary.BigEndian.PutUint32(packet[0:4], Magic)
	binary.BigEndian.PutUint32(packet[4:8], uint32(len(data)))
	copy(packet[8:], data)

	return packet
}

// sendPacket 发送数据包到指定地址
func (u *UDPDiscovery) sendPacket(packet []byte, addr net.IP) bool {
	if u.conn == nil {
		return false
	}

	dst := &net.UDPAddr{
		IP:   addr,
		Port: UDPDiscoveryPort,
	}

	u.conn.SetWriteDeadline(time.Now().Add(time.Second))
	_, err := u.conn.WriteToUDP(packet, dst)
	u.conn.SetWriteDeadline(time.Time{})

	if err != nil {
		log.Printf("Failed to send packet to %s: %v", dst, err)
		return false
	}
	log.Printf("Sent announcement to %s", dst)
	return true
}

// receiveAnnouncements 接收设备广播
func (u *UDPDiscovery) receiveAnnouncements() {
	buffer := make([]byte, 65536)

	for {
		select {
		case <-u.ctx.Done():
			return
		default:
		}

		if u.conn == nil {
			time.Sleep(time.Second)
			continue
		}

		u.conn.SetReadDeadline(time.Now().Add(time.Second))
		n, addr, err := u.conn.ReadFromUDP(buffer)
		u.conn.SetReadDeadline(time.Time{})

		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
				continue
			}
			log.Printf("Failed to read from UDP: %v", err)
			continue
		}

		if n < 8 {
			log.Printf("Received short packet from %s", addr)
			continue
		}

		u.processPacket(buffer[:n], addr)
	}
}

// processPacket 处理接收到的数据包
func (u *UDPDiscovery) processPacket(data []byte, addr *net.UDPAddr) {
	// 检查魔数
	magic := binary.BigEndian.Uint32(data[0:4])
	if magic != Magic {
		log.Printf("Received packet with wrong magic: %x from %s", magic, addr)
		return
	}

	// 解析数据长度
	dataLen := binary.BigEndian.Uint32(data[4:8])
	if uint32(len(data)) < 8+dataLen {
		log.Printf("Received truncated packet from %s", addr)
		return
	}

	// 解析设备信息
	var deviceInfo DeviceInfo
	err := json.Unmarshal(data[8:8+dataLen], &deviceInfo)
	if err != nil {
		log.Printf("Failed to unmarshal device info from %s: %v", addr, err)
		return
	}

	// 忽略自己的广播
	if deviceInfo.ID == u.deviceInfo.ID {
		return
	}

	log.Printf("Received announcement from %s for device %s", addr, deviceInfo.ID)

	// 更新缓存并通知新设备
	u.updateCache(deviceInfo)
}

// updateCache 更新设备缓存
func (u *UDPDiscovery) updateCache(deviceInfo DeviceInfo) {
	u.cacheMutex.Lock()
	defer u.cacheMutex.Unlock()

	// 检查是否是新设备或设备信息已更新
	cached, exists := u.cache[deviceInfo.ID]
	isNewDevice := !exists ||
		time.Since(cached.timestamp) > CacheLifeTime ||
		cached.info.InstanceID != deviceInfo.InstanceID

	// 更新缓存
	u.cache[deviceInfo.ID] = cachedDevice{
		info:      deviceInfo,
		timestamp: time.Now(),
	}

	// 如果是新设备，通知回调函数
	if isNewDevice && u.onDeviceFound != nil {
		u.onDeviceFound(deviceInfo)
	}
}

// getBroadcastAddresses 获取所有网络接口的广播地址
func (u *UDPDiscovery) getBroadcastAddresses() []net.IP {
	var broadcastAddrs []net.IP

	interfaces, err := net.Interfaces()
	if err != nil {
		log.Printf("❌ 获取网络接口失败: %v", err)
		// 使用通用广播地址作为后备
		return []net.IP{net.IPv4(255, 255, 255, 255)}
	}

	log.Printf("🔍 扫描 %d 个网络接口...", len(interfaces))
	for _, iface := range interfaces {
		log.Printf("📋 接口: %s (状态: %v, 标志: %v)", iface.Name, iface.Flags&net.FlagRunning != 0, iface.Flags)

		if iface.Flags&net.FlagRunning == 0 || iface.Flags&net.FlagBroadcast == 0 {
			log.Printf("⏭️ 跳过接口 %s (未运行或无广播)", iface.Name)
			continue
		}

		addrs, err := iface.Addrs()
		if err != nil {
			log.Printf("❌ 获取接口 %s 地址失败: %v", iface.Name, err)
			continue
		}

		log.Printf("📍 接口 %s 有 %d 个地址", iface.Name, len(addrs))
		for _, addr := range addrs {
			if ipNet, ok := addr.(*net.IPNet); ok {
				if len(ipNet.IP) >= 4 && ipNet.IP.IsGlobalUnicast() && ipNet.IP.To4() != nil {
					broadcastAddr := u.calculateBroadcastAddress(ipNet)
					broadcastAddrs = append(broadcastAddrs, broadcastAddr)
					log.Printf("✅ 添加广播地址: %s (来自 %s)", broadcastAddr, ipNet)
				}
			}
		}
	}

	if len(broadcastAddrs) == 0 {
		log.Printf("⚠️ 未找到有效的广播地址，使用通用广播地址")
		// 使用通用广播地址作为后备
		broadcastAddrs = append(broadcastAddrs, net.IPv4(255, 255, 255, 255))
	}

	log.Printf("🌐 最终广播地址列表: %v", broadcastAddrs)
	return broadcastAddrs
}

// calculateBroadcastAddress 计算广播地址
func (u *UDPDiscovery) calculateBroadcastAddress(ipNet *net.IPNet) net.IP {
	broadcast := make(net.IP, len(ipNet.IP))
	copy(broadcast, ipNet.IP)

	// 确保 mask 长度与 IP 长度匹配
	maskLen := len(ipNet.Mask)
	ipLen := len(ipNet.IP)

	// 使用较小的长度来避免越界
	loopLen := maskLen
	if ipLen < maskLen {
		loopLen = ipLen
	}

	for i := 0; i < loopLen; i++ {
		broadcast[i] = ipNet.IP[i] | ^ipNet.Mask[i]
	}

	return broadcast
}

// GetDiscoveredDevices 获取已发现的设备列表
func (u *UDPDiscovery) GetDiscoveredDevices() []DeviceInfo {
	u.cacheMutex.RLock()
	defer u.cacheMutex.RUnlock()

	var devices []DeviceInfo
	now := time.Now()

	for _, cached := range u.cache {
		if now.Sub(cached.timestamp) < CacheLifeTime {
			devices = append(devices, cached.info)
		}
	}

	return devices
}

// GetDeviceInfo 获取当前设备信息
func (u *UDPDiscovery) GetDeviceInfo() DeviceInfo {
	return u.deviceInfo
}

// SetDeviceName 设置设备名称
func (u *UDPDiscovery) SetDeviceName(name string) {
	u.deviceInfo.Name = name
	log.Printf("📝 设备名称已更新为: %s", name)
}

// GetDeviceName 获取设备名称
func (u *UDPDiscovery) GetDeviceName() string {
	return u.deviceInfo.Name
}

// SendManualBroadcast 手动发送一次UDP广播
func (u *UDPDiscovery) SendManualBroadcast() error {
	log.Println("📡 手动发送UDP广播...")

	if u.conn == nil {
		return fmt.Errorf("UDP连接未初始化")
	}

	// 调用现有的广播方法
	u.broadcastDeviceInfo()

	log.Println("✅ 手动UDP广播发送完成")
	return nil
}
